/*
* main.cpp
*
*  Created on: Apr 1, 2012
*      Author: daniel
*/
#include <iostream>
#include <fstream>
#include <string>
#include <bitset>

using namespace std;

//Declare Functions
void funcALU (int ALU_OP, int ALU_A, int ALU_B);
void Fetch (int memLoc);
void Decode ( );
void Execute ( );
void MemAccess ();
void WriteBack ();
void ControlUnit (int opcode);
//void FowardUnit (...);
//void HazardDetectionUnit (...);
int ALUControlUnit (int ALU_OP, int ALU_FUNC);

//Variables
int Memory[512] = {1}; //mem
int instrMem[512]; //instruction mem
int instrCount = 0;
int regArray [8] = {0,1,2,3,4,5,6,7}; //registers
struct IFID; //IFID buffer
struct IDEX; //IDEX buffer
struct EXMEM; //EXMEM buffer
struct MEMWB; //MEMWB buffer
// Control Signals
int PCSrc, regWriteData, regWriteRegister, branchAddress, memLoc, PCNext;

//bitmasks for decode
int opcodeBitmask = 0xF000;
int rsBitmask = 0x0E00;
int rtBitmask = 0x01C0;
int rdBitmask = 0x0038;
int funcBitmask = 0x0007;
int jumpBitmask = 0x0FFF;
int immediateBitmask = 0x003F;

//IFID buffer struct.
struct IFID {
	int instruction;
	int programCounter;
} ifidBuffer;
//IDEX Buffer
struct IDEX {
	int readDataOne;
	int readDataTwo;
	int programCounter;
	int registerRs;
	int registerRt;
	int registerRd;
	int signExtend;
	int opcode;
	//Store all the control unit outputs
	int RegDst;
	int Jump;
	int Branch;
	int MemRead;
	int MemtoReg;
	int ALUOp;
	int MemWrite;
	int ALUSrc;
	int RegWrite;
} idexBuffer;
//EXMEM buffer
struct EXMEM {
	int readDataTwo;
	int programCounter;
	int signExtend;
	int destinationReg;
	int branchAddress;
	int ALUZero;
	int ALUResult;
	//Store all the control unit outputs
	int Jump;
	int Branch;
	int MemRead;
	int MemtoReg;
	int MemWrite;
	int RegWrite;
} exmemBuffer;
//MEMWB Buffer
struct MEMWB {
	int readDataTwo;
	int programCounter;
	int signExtend;
	int destinationReg;
	int ALUResult;
	int memoryData;
	//Store all the control unit outputs
	int MemtoReg;
	int RegWrite;
} memwbBuffer;


int main ( ) { // Clear Memory and Register contents
	//Initialize Memory contents according to the project handout.
	Memory [0] = 1;
	Memory [2] = -1;
	//Read instructions from the input file and store them into memory.

	ifstream myfile ("C:/Users/Tommy/Dropbox/Team DRT/Test.txt");
	string line;
	if (myfile.is_open())
	{
		int i = 0;
		while ( myfile.good() )
		{
			getline (myfile,line);
			instrMem[i] = bitset<16>(line).to_ulong();
			cout << i << ": " << line << " " << instrMem[i] << endl;
			i++;
			instrCount++;
		}
		myfile.close();
	}

	//Print initial memory and register contents ......
	// Set PC and execute program by fetching instruction from the memory Unit until the program ends. Looping. ......

	/*if (PCSrc == 0)
	{
	Fetch(PCNext);
	}
	if (PCSrc == 1)
	{
	Fetch(branchAddress);
	}
	*/
	for (int i =0; i < instrCount; i++) {
		Fetch(i);
		Decode();

		if (idexBuffer.Jump == 1) {
			continue;
		}

		Execute();
		MemAccess();
		WriteBack();

		for (int i = 0; i < 8; i++) 
			cout << "Reg " << i << ": " << regArray[i] << endl;
		int j = 0;
	}
	return 0;
}
//Fetch Stage
void Fetch (int memLoc)
{

	ifidBuffer.instruction = instrMem[memLoc]; //Grab instruction and put it in IF/ID buffer
	ifidBuffer.programCounter = memLoc + 1; //increment PC and put in IF/ID buffer
	PCNext = ifidBuffer.programCounter;
	cout << "PC Counter at " << memLoc << endl;
}

void Decode()
{
	cout << "Entering Decode Stage" << endl;
	//decode the machine code into individual parts
	int instruction = ifidBuffer.instruction;
	int opcode = (instruction & opcodeBitmask) >> 12;
	int readRegisterOne = (instruction & rsBitmask) >> 9;
	int readRegisterTwo = (instruction & rtBitmask) >> 6;
	int writeRegister = (instruction & rdBitmask) >> 3;
	int jump = (instruction & jumpBitmask);
	int func = (instruction & funcBitmask);
	int signExtend = (instruction & immediateBitmask);
	cout << "Instruction: " << instruction << endl;
	cout << "Opcode: " << opcode << endl;
	cout << "readRegisterOne: " << readRegisterOne << endl;
	cout << "readRegisterTwo: " << readRegisterTwo << endl;
	cout << "writeRegister: " << writeRegister << endl;
	cout << "Func Code: " << func << endl;
	cout << "signExtend: " << signExtend << endl;
	ControlUnit(opcode);

	if (idexBuffer.Jump == 1) {
		PCNext = jump;
	}
	//Read from registers and put into ID/EX buffer

	idexBuffer.readDataOne = regArray[readRegisterOne];
	idexBuffer.readDataTwo = regArray[readRegisterTwo];

	cout << "Reg1Val: " << regArray[readRegisterOne] << endl;
	cout << "Reg2Val: " << regArray[readRegisterTwo] << endl;

	//Pass on program counter info to ID/EX
	idexBuffer.programCounter = ifidBuffer.programCounter;
	//Store this stuff in ID/EX buffer
	idexBuffer.registerRs = readRegisterOne;
	idexBuffer.registerRt = readRegisterTwo;
	idexBuffer.registerRd = writeRegister;
	idexBuffer.signExtend = signExtend;
	idexBuffer.opcode = opcode;
}

void Execute ( )
{
	cout << endl << "Execute State" << endl;
	int func = idexBuffer.signExtend & 0x0007;
	if (idexBuffer.RegDst == 0)
	{
		exmemBuffer.destinationReg = idexBuffer.registerRt;
	}
	else
	{
		exmemBuffer.destinationReg = idexBuffer.registerRd;
	}
	cout << "Destination Reg: " << exmemBuffer.destinationReg << endl;
	cout << "ALUOP: " << idexBuffer.ALUOp << endl;
	int aluop;

	if (idexBuffer.ALUOp == 0) {
		aluop = ALUControlUnit(idexBuffer.ALUOp, idexBuffer.opcode);
	}
	else {
		aluop = ALUControlUnit(idexBuffer.ALUOp, func);
	}

	cout << "ALU Control Output: " << aluop << endl;
	exmemBuffer.readDataTwo = idexBuffer.readDataTwo;
	cout << "IDEXBUFFER ALUSRC: " << idexBuffer.ALUSrc << endl;
	if (idexBuffer.ALUSrc == 0)
	{
		funcALU(aluop, idexBuffer.readDataOne, idexBuffer.readDataTwo);
	}
	else
	{
		cout << "ALUOP: " << aluop << " RD1: " << idexBuffer.readDataOne << " RD2: " << idexBuffer.signExtend << endl;
		funcALU(aluop, idexBuffer.readDataOne, idexBuffer.signExtend);
	}
	int branchAddress = idexBuffer.signExtend << 2;
	exmemBuffer.MemtoReg = idexBuffer.MemtoReg;
	exmemBuffer.branchAddress = idexBuffer.programCounter + branchAddress;
	memwbBuffer.ALUResult = exmemBuffer.ALUResult;
	exmemBuffer.RegWrite = idexBuffer.RegWrite;
	memwbBuffer.destinationReg = exmemBuffer.destinationReg;
	cout << "ALU Input 1: " << idexBuffer.readDataOne << endl;
	cout << "ALU Input 2: " << idexBuffer.readDataTwo << endl;
	cout << "ALU Result: " << exmemBuffer.ALUResult << endl;

}


void MemAccess ()
{
	branchAddress = exmemBuffer.branchAddress;
	PCSrc = exmemBuffer.Branch & exmemBuffer.ALUZero;
	if (exmemBuffer.MemWrite == 1)
	{
		Memory[exmemBuffer.ALUResult] = exmemBuffer.readDataTwo;
	}
	if (exmemBuffer.MemRead == 1)
	{
		memwbBuffer.memoryData = Memory[exmemBuffer.ALUResult];
	}
	memwbBuffer.ALUResult = exmemBuffer.ALUResult;
	memwbBuffer.MemtoReg = exmemBuffer.MemtoReg;
	memwbBuffer.RegWrite = exmemBuffer.RegWrite;
	cout << "EXMEM ALU RESULT: " << exmemBuffer.ALUResult << endl;

}
void WriteBack ()
{
	cout << "Write Back stuff" << endl;
	cout << "MEMWB ALU RESULT: " << memwbBuffer.ALUResult << endl;
	if(memwbBuffer.MemtoReg == 1)
	{
		regWriteData = memwbBuffer.memoryData;
	}
	if(memwbBuffer.MemtoReg == 0)
	{
		regWriteData = memwbBuffer.ALUResult;
	}
	if (memwbBuffer.RegWrite == 1)
	{
		regWriteData = memwbBuffer.ALUResult;
		regArray[memwbBuffer.destinationReg] = regWriteData;
	}
	cout << "RegWrite: " << memwbBuffer.RegWrite << endl;
	cout << "Write Reg: " << memwbBuffer.destinationReg << endl;
	cout << "RegWriteData: " << regWriteData << endl;
	cout << "New Reg Value: " << regArray[memwbBuffer.destinationReg] << endl;
	cout << "Updated register: " << memwbBuffer.destinationReg << endl;
}
void ControlUnit(int opcode)
{
	if (opcode == 0) //R format
	{
		idexBuffer.RegDst = 1;
		idexBuffer.Jump = 0;
		idexBuffer.Branch = 0;
		idexBuffer.MemRead = 0;
		idexBuffer.MemtoReg = 0;
		idexBuffer.ALUOp = 2;
		idexBuffer.MemWrite = 1;
		idexBuffer.ALUSrc = 0;
		idexBuffer.RegWrite = 1;
	}
	else if (opcode == 1) //jump
	{
		idexBuffer.RegDst = 0;
		idexBuffer.Jump = 1;
		idexBuffer.Branch = 0;
		idexBuffer.MemRead = 0;
		idexBuffer.MemtoReg = 0;
		idexBuffer.ALUOp = 0;
		idexBuffer.MemWrite = 0;
		idexBuffer.ALUSrc = 0;
		idexBuffer.RegWrite = 0;
	}
	else if (opcode == 2) //sw Store Word
	{
		//idexBuffer.RegDst = 0; DON'T CARE
		idexBuffer.Jump = 0;
		idexBuffer.Branch = 0;
		idexBuffer.MemRead = 0;
		//idexBuffer.MemtoReg = 0; DON'T CARE
		idexBuffer.ALUOp = 0;
		idexBuffer.MemWrite = 1;
		idexBuffer.ALUSrc = 1;
		idexBuffer.RegWrite = 0;
	}
	else if (opcode == 3) //lw Load Word
	{
		idexBuffer.RegDst = 0;
		idexBuffer.Jump = 0;
		idexBuffer.Branch = 0;
		idexBuffer.MemRead = 1;
		idexBuffer.MemtoReg = 1;
		idexBuffer.ALUOp = 0;
		idexBuffer.MemWrite = 0;
		idexBuffer.ALUSrc = 1;
		idexBuffer.RegWrite = 1;
	}
	else if (opcode == 4) //beq Branch on Equal
	{
		//idexBuffer.RegDst = 0; DON'T CARE
		idexBuffer.Jump = 0;
		idexBuffer.Branch = 1;
		idexBuffer.MemRead = 0;
		//idexBuffer.MemtoReg = 0; DON'T CARE
		idexBuffer.ALUOp = 1;
		idexBuffer.MemWrite = 0;
		idexBuffer.ALUSrc = 0;
		idexBuffer.RegWrite = 0;
	}
	else if (opcode == 5) //noop No Operation
	{
		//idexBuffer.RegDst = 0;
		//idexBuffer.Jump = 0;
		//idexBuffer.Branch = 0;
		//idexBuffer.MemRead = 0;
		//idexBuffer.MemtoReg = 0;
		//idexBuffer.ALUOp = 0;
		//idexBuffer.MemWrite = 0;
		//idexBuffer.ALUSrc = 0;
		//idexBuffer.RegWrite = 0;
	}
	else if (opcode == 8) //andi And Immediate
	{
		idexBuffer.RegDst = 0;
		idexBuffer.Jump = 0;
		idexBuffer.Branch = 0;
		idexBuffer.MemRead = 0;
		idexBuffer.MemtoReg = 0;
		idexBuffer.ALUOp = 0;
		idexBuffer.MemWrite = 0;
		idexBuffer.ALUSrc = 1;
		idexBuffer.RegWrite = 1;
	}
	else if (opcode == 9) //ori Or Immediate
	{
		idexBuffer.RegDst = 0;
		idexBuffer.Jump = 0;
		idexBuffer.Branch = 0;
		idexBuffer.MemRead = 0;
		idexBuffer.MemtoReg = 0;
		idexBuffer.ALUOp = 0;
		idexBuffer.MemWrite = 0;
		idexBuffer.ALUSrc = 1;
		idexBuffer.RegWrite = 1;
	}
	else if (opcode == 10) //addi Add Immediate
	{
		idexBuffer.RegDst = 0;
		idexBuffer.Jump = 0;
		idexBuffer.Branch = 0;
		idexBuffer.MemRead = 0;
		idexBuffer.MemtoReg = 0;
		idexBuffer.ALUOp = 0;
		idexBuffer.MemWrite = 0;
		idexBuffer.ALUSrc = 1;
		idexBuffer.RegWrite = 1;
	}
	else if (opcode == 11) //lui Load Upper Immediate
	{
		idexBuffer.RegDst = 0;
		idexBuffer.Jump = 0;
		idexBuffer.Branch = 0;
		idexBuffer.MemRead = 0;
		idexBuffer.MemtoReg = 0;
		idexBuffer.ALUOp = 0;
		idexBuffer.MemWrite = 0;
		idexBuffer.ALUSrc = 1;
		idexBuffer.RegWrite = 1;
	}
	else //Something terribly wrong has happened.
	{

	}
	//END CONTROL UNIT
}

void funcALU (int ALU_OP, int ALU_A, int ALU_B)
{
	int val = 0;

	if (ALU_OP == 0) // AND
	{
		val = ALU_A & ALU_B;
	}
	else if (ALU_OP == 1) // OR
	{
		val = ALU_A | ALU_B;
	}
	else if (ALU_OP == 2) // ADD
	{
		val = ALU_A + ALU_B;
	}
	else if (ALU_OP == 3) // SUBTRACT
	{
		val = ALU_A - ALU_B;
	}
	else if (ALU_OP == 4) // SET LESS THAN
	{
		if (ALU_A < ALU_B)
		{
			val = 1;
		}
		else
		{
			val = 0;
		}
	}
	else if (ALU_OP == 5) // EXCLUSIVE OR
	{
		val = ALU_A ^ ALU_B;
	}
	else if (ALU_OP == 6) // SHIFT RIGHT LOGICAL
	{
		val = ALU_A >> ALU_B;
	}
	else if (ALU_OP == 7) // SHIFT LEFT LOGICAL
	{
		val = ALU_A << ALU_B;
	}
	else if (ALU_OP == 8) // LOAD UPPER
	{
		val = 0;
		val = (ALU_B & 0xFF) >> 8;
	}

	exmemBuffer.ALUResult = val;

	if (val == 0)
	{
		exmemBuffer.ALUZero = 1;
	}
}

int ALUControlUnit (int ALU_OP, int ALU_FUNC)
{
	if (ALU_OP == 1 || ALU_OP == 2)
	{
		return ALU_FUNC;
	}
	else if (ALU_OP == 0)
	{
		if (ALU_FUNC == 3) // LOAD WORD
		{
			return 2;
		}
		else if (ALU_FUNC == 8) //andi And Immediate
		{
			return 0;
		}
		else if (ALU_FUNC == 9) //ori Or Immediate
		{
			return 1;
		}
		else if (ALU_FUNC == 10) //addi Add Immediate
		{
			return 2;
		}
		else if (ALU_FUNC == 11) //lui Load Upper Immediate
		{
			return 8;
		}
	}

	return -1;
}
